/*
 * KiRouter - a push-and-(sometimes-)shove PCB router
 *
 * Copyright (C) 2013-2014 CERN
 * Copyright (C) 2016 KiCad Developers, see AUTHORS.txt for contributors.
 * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __PNS_NODE_H
#define __PNS_NODE_H

#include <vector>
#include <list>
#include <unordered_set>
#include <unordered_map>

#include <core/optional.h>
#include <core/minoptmax.h>

#include <geometry/shape.h>
#include <geometry/shape_line_chain.h>
#include <geometry/shape_index.h>

#include "pns_item.h"
#include "pns_joint.h"
#include "pns_itemset.h"

namespace PNS {

class ARC;
class SEGMENT;
class LINE;
class SOLID;
class VIA;
class INDEX;
class ROUTER;
class NODE;

/**
 * RULE_RESOLVER
 *
 * An abstract function object, returning a design rule (clearance, diff pair gap, etc) required between two items.
 **/

enum class CONSTRAINT_TYPE
{
    CT_CLEARANCE = 1,
    CT_DIFF_PAIR_GAP = 2,
    CT_LENGTH = 3,
    CT_WIDTH = 4,
    CT_VIA_DIAMETER = 5,
    CT_VIA_HOLE = 6
};

struct CONSTRAINT
{
    CONSTRAINT_TYPE m_Type;
    MINOPTMAX<int> m_Value;
    bool m_Allowed;
    wxString m_RuleName;
    wxString m_FromName;
    wxString m_ToName;
};

class RULE_RESOLVER
{
public:
    virtual ~RULE_RESOLVER() {}

    virtual bool CollideHoles( const ITEM* aA, const ITEM* aB,
                               bool aNeedMTV, VECTOR2I* aMTV ) const = 0;

    virtual int Clearance( const ITEM* aA, const ITEM* aB ) = 0;
    //virtual int Clearance( int aNetCode ) const = 0;
    virtual int DpCoupledNet( int aNet ) = 0;
    virtual int DpNetPolarity( int aNet ) = 0;
    virtual bool DpNetPair( const ITEM* aItem, int& aNetP, int& aNetN ) = 0;

    virtual bool IsDiffPair( const ITEM* aA, const ITEM* aB ) = 0;

    virtual bool QueryConstraint( CONSTRAINT_TYPE aType, const PNS::ITEM* aItemA, const PNS::ITEM* aItemB, int aLayer, PNS::CONSTRAINT* aConstraint ) = 0;
    
    virtual wxString NetName( int aNet ) = 0;
};

/**
 * Struct OBSTACLE
 *
 * Holds an object colliding with another object, along with
 * some useful data about the collision.
 **/
struct OBSTACLE
{
    ///> Item we search collisions with
    const ITEM* m_head;

    ///> Item found to be colliding with m_head
    ITEM* m_item;

    ///> Hull of the colliding m_item
    SHAPE_LINE_CHAIN m_hull;

    ///> First and last intersection point between the head item and the hull
    ///> of the colliding m_item
    VECTOR2I m_ipFirst, m_ipLast;

    ///> ... and the distance thereof
    int m_distFirst, m_distLast;
};

/**
 * Struct OBSTACLE_VISITOR
 **/
class OBSTACLE_VISITOR {

public:

    OBSTACLE_VISITOR( const ITEM* aItem );

    virtual ~OBSTACLE_VISITOR()
    {
    }

    void SetWorld( const NODE* aNode, const NODE* aOverride = NULL );

    virtual bool operator()( ITEM* aCandidate ) = 0;

protected:

    bool visit( ITEM* aCandidate );

    ///> the item we are looking for collisions with
    const ITEM* m_item;

    ///> node we are searching in (either root or a branch)
    const NODE* m_node;

    ///> node that overrides root entries
    const NODE* m_override;

    ///> additional clearance
    int m_extraClearance;
};

/**
 * NODE
 *
 * Keeps the router "world" - i.e. all the tracks, vias, solids in a
 * hierarchical and indexed way.
 * Features:
 * - spatial-indexed container for PCB item shapes
 * - collision search & clearance checking
 * - assembly of lines connecting joints, finding loops and unique paths
 * - lightweight cloning/branching (for recursive optimization and shove
 * springback)
 **/
class NODE
{
public:
    typedef OPT<OBSTACLE>   OPT_OBSTACLE;
    typedef std::vector<ITEM*>          ITEM_VECTOR;
    typedef std::vector<OBSTACLE>       OBSTACLES;

    NODE();
    ~NODE();

    ///> Returns the expected clearance between items a and b.
    int GetClearance( const ITEM* aA, const ITEM* aB ) const;

    ///> Returns the pre-set worst case clearance between any pair of items
    int GetMaxClearance() const
    {
        return m_maxClearance;
    }

    ///> Sets the worst-case clerance between any pair of items
    void SetMaxClearance( int aClearance )
    {
        m_maxClearance = aClearance;
    }

    ///> Assigns a clerance resolution function object
    void SetRuleResolver( RULE_RESOLVER* aFunc )
    {
        m_ruleResolver = aFunc;
    }

    RULE_RESOLVER* GetRuleResolver() const
    {
        return m_ruleResolver;
    }

    ///> Returns the number of joints
    int JointCount() const
    {
        return m_joints.size();
    }

    ///> Returns the number of nodes in the inheritance chain (wrs to the root node)
    int Depth() const
    {
        return m_depth;
    }

    /**
     * Function QueryColliding()
     *
     * Finds items collliding (closer than clearance) with the item aItem.
     * @param aItem item to check collisions against
     * @param aObstacles set of colliding objects found
     * @param aKindMask mask of obstacle types to take into account
     * @param aLimitCount stop looking for collisions after finding this number of colliding items
     * @return number of obstacles found
     */
    int QueryColliding( const ITEM*  aItem,
                        OBSTACLES&   aObstacles,
                        int          aKindMask = ITEM::ANY_T,
                        int          aLimitCount = -1,
                        bool         aDifferentNetsOnly = true,
                        int          aForceClearance = -1 );

    int QueryJoints( const BOX2I& aBox, std::vector<JOINT*> & aJoints, int aLayerMask = -1, int aKindMask = ITEM::ANY_T);

    int QueryColliding( const ITEM* aItem,
                         OBSTACLE_VISITOR& aVisitor
                      );

    /**
     * Function NearestObstacle()
     *
     * Follows the line in search of an obstacle that is nearest to the starting to the line's starting
     * point.
     * @param aItem the item to find collisions with
     * @param aKindMask mask of obstacle types to take into account
     * @return the obstacle, if found, otherwise empty.
     */
    OPT_OBSTACLE NearestObstacle( const LINE*             aItem,
                                  int                     aKindMask = ITEM::ANY_T,
                                  const std::set<ITEM*>*  aRestrictedSet = NULL );

    /**
     * Function CheckColliding()
     *
     * Checks if the item collides with anything else in the world,
     * and if found, returns the obstacle.
     * @param aItem the item to find collisions with
     * @param aKindMask mask of obstacle types to take into account
     * @return the obstacle, if found, otherwise empty.
     */
    OPT_OBSTACLE CheckColliding( const ITEM*     aItem,
                                 int             aKindMask = ITEM::ANY_T );


    /**
     * Function CheckColliding()
     *
     * Checks if any item in the set collides with anything else in the world,
     * and if found, returns the obstacle.
     * @param aSet set of items to find collisions with
     * @param aKindMask mask of obstacle types to take into account
     * @return the obstacle, if found, otherwise empty.
     */
    OPT_OBSTACLE CheckColliding( const ITEM_SET&  aSet,
                                 int              aKindMask = ITEM::ANY_T );


    /**
     * Function CheckColliding()
     *
     * Checks if 2 items collide.
     * and if found, returns the obstacle.
     * @param aItemA  first item to find collisions with
     * @param aItemB  second item to find collisions with
     * @param aKindMask mask of obstacle types to take into account
     * @return the obstacle, if found, otherwise empty.
     */
    bool CheckColliding( const ITEM*    aItemA,
                         const ITEM*    aItemB,
                         int            aKindMask = ITEM::ANY_T,
                         int            aForceClearance = -1 );

    /**
     * Function HitTest()
     *
     * Finds all items that contain the point aPoint.
     * @param aPoint the point
     * @return the items
     */
    const ITEM_SET HitTest( const VECTOR2I& aPoint ) const;

    /**
     * Function Add()
     *
     * Adds an item to the current node.
     * @param aSegment item to add
     * @param aAllowRedundant if true, duplicate items are allowed (e.g. a segment or via
     * @return true if added
     * at the same coordinates as an existing one)
     */
    bool Add( std::unique_ptr< SEGMENT > aSegment, bool aAllowRedundant = false );
    void Add( std::unique_ptr< SOLID >   aSolid );
    void Add( std::unique_ptr< VIA >     aVia );
    void Add( std::unique_ptr< ARC >     aArc );

    void Add( LINE& aLine, bool aAllowRedundant = false );

private:
    void Add( std::unique_ptr< ITEM > aItem, bool aAllowRedundant = false );

public:
    /**
     * Function Remove()
     *
     * Just as the name says, removes an item from this branch.
     */
    void Remove( ARC* aArc );
    void Remove( SOLID* aSolid );
    void Remove( VIA* aVia );
    void Remove( SEGMENT* aSegment );
    void Remove( ITEM* aItem );

public:
    /**
     * Function Remove()
     *
     * Just as the name says, removes a line from this branch.
     * @param aLine item to remove
     */
    void Remove( LINE& aLine );

    /**
     * Function Replace()
     *
     * Just as the name says, replaces an item with another one.
     * @param aOldItem item to be removed
     * @param aNewItem item add instead
     */
    void Replace( ITEM* aOldItem, std::unique_ptr< ITEM > aNewItem );
    void Replace( LINE& aOldLine, LINE& aNewLine );

    /**
     * Function Branch()
     *
     * Creates a lightweight copy (called branch) of self that tracks
     * the changes (added/removed items) wrs to the root. Note that if there are
     * any branches in use, their parents must NOT be deleted.
     * @return the new branch
     */
    NODE* Branch();

    /**
     * Function AssembleLine()
     *
     * Follows the joint map to assemble a line connecting two non-trivial
     * joints starting from segment aSeg.
     * @param aSeg the initial segment
     * @param aOriginSegmentIndex index of aSeg in the resulting line
     * @return the line
     */
    const LINE AssembleLine( LINKED_ITEM* aSeg, int* aOriginSegmentIndex = NULL,
                                 bool aStopAtLockedJoints = false );

    ///> Prints the contents and joints structure
    void Dump( bool aLong = false );

    /**
     * Function GetUpdatedItems()
     *
     * Returns the lists of items removed and added in this branch, with
     * respect to the root branch.
     * @param aRemoved removed items
     * @param aAdded added items
     */
    void GetUpdatedItems( ITEM_VECTOR& aRemoved, ITEM_VECTOR& aAdded );

    /**
     * Function Commit()
     *
     * Applies the changes from a given branch (aNode) to the root branch. Called on
     * a non-root branch will fail. Calling commit also kills all children nodes of the root branch.
     * @param aNode node to commit changes from
     */
    void Commit( NODE* aNode );

    /**
     * Function FindJoint()
     *
     * Searches for a joint at a given position, layer and belonging to given net.
     * @return the joint, if found, otherwise empty
     */
    JOINT* FindJoint( const VECTOR2I& aPos, int aLayer, int aNet );

    void LockJoint( const VECTOR2I& aPos, const ITEM* aItem, bool aLock );

    /**
     * Function FindJoint()
     *
     * Searches for a joint at a given position, linked to given item.
     * @return the joint, if found, otherwise empty
     */
    JOINT* FindJoint( const VECTOR2I& aPos, const ITEM* aItem )
    {
        return FindJoint( aPos, aItem->Layers().Start(), aItem->Net() );
    }

#if 0
    void MapConnectivity( JOINT* aStart, std::vector<JOINT*> & aFoundJoints );

    ITEM* NearestUnconnectedItem( JOINT* aStart, int* aAnchor = NULL,
                                      int aKindMask = ITEM::ANY_T);

#endif

    ///> finds all lines between a pair of joints. Used by the loop removal procedure.
    int FindLinesBetweenJoints( JOINT&                  aA,
                                JOINT&                  aB,
                                std::vector<LINE>&      aLines );

    ///> finds the joints corresponding to the ends of line aLine
    void FindLineEnds( const LINE& aLine, JOINT& aA, JOINT& aB );

    ///> Destroys all child nodes. Applicable only to the root node.
    void KillChildren();

    void AllItemsInNet( int aNet, std::set<ITEM*>& aItems, int aKindMask = -1 );

    void ClearRanks( int aMarkerMask = MK_HEAD | MK_VIOLATION | MK_ALT_SHAPE );

    void RemoveByMarker( int aMarker );

    ITEM* FindItemByParent( const BOARD_ITEM* aParent );

    bool HasChildren() const
    {
        return !m_children.empty();
    }

    NODE* GetParent() const
    {
        return m_parent;
    }

    ///> checks if this branch contains an updated version of the m_item
    ///> from the root branch.
    bool Overrides( ITEM* aItem ) const
    {
        return m_override.find( aItem ) != m_override.end();
    }

private:
    struct DEFAULT_OBSTACLE_VISITOR;
    typedef std::unordered_multimap<JOINT::HASH_TAG, JOINT, JOINT::JOINT_TAG_HASH> JOINT_MAP;
    typedef JOINT_MAP::value_type TagJointPair;

    /// nodes are not copyable
    NODE( const NODE& aB );
    NODE& operator=( const NODE& aB );

    ///> tries to find matching joint and creates a new one if not found
    JOINT& touchJoint( const VECTOR2I&     aPos,
                       const LAYER_RANGE&  aLayers,
                       int                 aNet );

    ///> touches a joint and links it to an m_item
    void linkJoint( const VECTOR2I& aPos, const LAYER_RANGE& aLayers, int aNet, ITEM* aWhere );

    ///> unlinks an item from a joint
    void unlinkJoint( const VECTOR2I& aPos, const LAYER_RANGE& aLayers, int aNet, ITEM* aWhere );

    ///> helpers for adding/removing items
    void addSolid( SOLID* aSeg );
    void addSegment( SEGMENT* aSeg );
    void addVia( VIA* aVia );
    void addArc( ARC* aVia );

    void removeLine( LINE& aLine );
    void removeSolidIndex( SOLID* aSeg );
    void removeSegmentIndex( SEGMENT* aSeg );
    void removeViaIndex( VIA* aVia );
    void removeArcIndex( ARC* aVia );

    void doRemove( ITEM* aItem );
    void unlinkParent();
    void releaseChildren();
    void releaseGarbage();
    void rebuildJoint( JOINT* aJoint, ITEM* aItem );

    bool isRoot() const
    {
        return m_parent == NULL;
    }

    SEGMENT* findRedundantSegment( const VECTOR2I& A, const VECTOR2I& B,
                                   const LAYER_RANGE & lr, int aNet );
    SEGMENT* findRedundantSegment( SEGMENT* aSeg );

    ARC* findRedundantArc( const VECTOR2I& A, const VECTOR2I& B,
                                   const LAYER_RANGE & lr, int aNet );
    ARC* findRedundantArc( ARC* aSeg );

    ///> scans the joint map, forming a line starting from segment (current).
    void followLine( LINKED_ITEM* aCurrent, int aScanDirection, int& aPos, int aLimit, VECTOR2I* aCorners,
            LINKED_ITEM** aSegments, bool& aGuardHit, bool aStopAtLockedJoints );

    ///> hash table with the joints, linking the items. Joints are hashed by
    ///> their position, layer set and net.
    JOINT_MAP m_joints;

    ///> node this node was branched from
    NODE* m_parent;

    ///> root node of the whole hierarchy
    NODE* m_root;

    ///> list of nodes branched from this one
    std::set<NODE*> m_children;

    ///> hash of root's items that have been changed in this node
    std::unordered_set<ITEM*> m_override;

    ///> worst case item-item clearance
    int m_maxClearance;

    ///> Design rules resolver
    RULE_RESOLVER* m_ruleResolver;

    ///> Geometric/Net index of the items
    INDEX* m_index;

    ///> depth of the node (number of parent nodes in the inheritance chain)
    int m_depth;

    std::unordered_set<ITEM*> m_garbageItems;
};

}

#endif
